package com.gollum.jpa.domain;

import com.fasterxml.jackson.annotation.JsonIgnore;

import com.gollum.jpa.util.QueryUtils;
import com.gollum.jpa.util.SortUtils;
import com.gollum.jpa.util.StrUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.springframework.data.domain.Sort;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.util.*;

/**
 * 分页返回数据合集
 *
 * @author Herther
 * @version 1.0.0
 * @ClassName Page.java
 * @createTime 2022年08月07日 18:36:00
 */

public class Page<T> implements Iterable<T>, Serializable {

    private static final long serialVersionUID = -73812973891278L;

    /** 起始页 **/
    private int start;
    /** 多少条记录 **/
    private int limit;
    /** 数据 **/
    private List<T> records;
    /** 总数 **/
    private long totalCount;

    /** 不序列化sort字段 **/
    @JsonIgnore
    private Sort sort;

    /** 排序字符串 **/
    @JsonIgnore
    private String sortStr;

    /** 排序规则 **/
    @JsonIgnore
    private String dir;

    public Page() {
        this(0, 15);
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
                .append("start", getStart())
                .append("limit", getLimit())
                .append("records", getRecords())
                .append("totalCount", getTotalCount())
                .append("sort", getSort())
                .append("sortStr", getSortStr())
                .append("dir", getDir())
                .toString();
    }


    public Page(int start, int limit) {
        this.sort = Sort.unsorted();
        this.setStart(start);
        this.setLimit(limit);
        this.records = Collections.emptyList();
    }

    public Page(int start, int limit, Sort sort) {
        this(start, limit);
        this.sort = sort;
    }

    public Page(int start, int limit, String sort, String dir) {
        this(start, limit);
        if (sort != null && dir != null) {
            if (Sort.Direction.fromString(dir).equals(Sort.Direction.DESC)) {
                this.desc(sort);
            } else {
                this.asc(sort);
            }
            this.sortStr = sort;
            this.dir = dir;
        }

    }

    public Page(int start, int limit, String sort, Sort.Direction dir) {
        this(start, limit);
        if (sort != null && dir != null) {
            if (dir.equals(Sort.Direction.DESC)) {
                this.desc(sort);
                this.dir = "desc";
            } else {
                this.asc(sort);
                this.dir = "asc";
            }
            this.sortStr = sort;
        }

    }

    public static <T> Page<T> from(Integer pageNo, Integer pageSize) {
        pageNo = pageNo != null && pageNo >= 1 ? pageNo : 1;
        pageSize = pageSize != null && pageSize >= 1 ? pageSize : 15;
        Page<T> page = new Page((pageNo - 1) * pageSize, pageSize);
        return page;
    }

    public static <T> Page<T> from(Integer pageNo, Integer pageSize, List<String> sorts) {
        Page<T> page = from(pageNo, pageSize);
        if (!CollectionUtils.isEmpty(sorts)) {
            page.setSort(QueryUtils.buildSort((String[])sorts.toArray(new String[0])));
        }
        return page;
    }

    public static <T> Page<T> from(Integer pageNo, Integer pageSize,String sorts) {
        Page<T> page = from(pageNo, pageSize);
        if (StrUtils.isNotBlank(sorts)) {
            String[] split = sorts.split(",");
            page.setSort(QueryUtils.buildSort(split));
        }
        return page;
    }

    public int getNumber() {
        return this.getStart() / this.getLimit() + 1;
    }

    public int getStart() {
        return this.start;
    }

    public void setStart(int start) {
        this.start = Math.max(0, start);
    }

    public int getLimit() {
        return this.limit;
    }

    public void setLimit(int limit) {
        this.limit = Math.min(1000, limit);
    }

    public String getSortStr() {
        return this.sortStr;
    }

    public void setSortStr(String sortStr) {
        this.sortStr = sortStr;
    }

    public String getDir() {
        return this.dir;
    }

    public void setDir(String dir) {
        this.dir = dir;
    }

    public List<T> getRecords() {
        return this.records;
    }

    public void setRecords(List<T> records) {
        this.records = records;
    }

    public long getTotalCount() {
        return this.totalCount;
    }

    public void setTotalCount(long totalCount) {
        this.totalCount = totalCount;
    }

    public Sort getSort() {
        if ((this.sort == null || !this.sort.isSorted()) && StringUtils.isNotBlank(this.sortStr) && StringUtils.isNotBlank(this.dir)) {
            if (this.dir.equalsIgnoreCase("DESC")) {
                this.desc(this.sortStr);
            } else {
                this.asc(this.sortStr);
            }
        }

        return this.sort;
    }

    public void setSort(Sort sort) {
        this.sort = sort;
    }

    public Iterator<T> iterator() {
        return this.records.iterator();
    }

    public void asc(String... name) {
        Sort.Order[] orders = new Sort.Order[name.length];

        for(int i = 0; i < name.length; ++i) {
            orders[i] = new Sort.Order(Sort.Direction.ASC, name[i]);
        }
        this.orderBy(orders);
    }

    public void desc(String... name) {
        Sort.Order[] orders = new Sort.Order[name.length];

        for(int i = 0; i < name.length; ++i) {
            orders[i] = new Sort.Order(Sort.Direction.DESC, name[i]);
        }
        this.orderBy(orders);
    }

    public void orderBy(Sort.Order... orders) {
        if (orders.length > 0) {
            this.sort = this.sort.and(Sort.by(orders));
        }
    }
}